Renderer = function () {

};

Renderer.ids = {
    // Task name
    TASK_NAME_ID:'#task_name',
    TASK_DESCR_ID:'#task_description',
    // Select task combobox
    TASK_SELECT_ID:'#task_select',
    // Final state table
    FINAL_STATE_ID:'#final_state',
    HEAD_INTO_CLASS:'head_into',
    TAPE_TBL_ID:'#current_state',
    // Rules table
    RULES_TBL_ID:"#rules_table",
    RULES_TBL_THEAD_ID:"#rules_table thead",
    RULES_TBL_TBODY_ID:"#rules_table tbody",
    REMOVE_STATE_PREFIX:"remove_state_",
    REMOVE_STATE_CLASS:"remove_state",
    STATE_NAME_CLASS:"state_name",
    MOVE_CMB_CLASS:"move_select",
    SYMBOL_CMB_CLASS:"symbol_select",
    STATE_CMB_CLASS:"state_select",
    RULES_TBL_TD_STATE:"state_",
    RULES_TBL_TD_SYMBOL:"_symbol_",
    // Rules controls
    MOVE_SELECT_CLASS:'.move_select',
    SYMBOL_SELECT_CLASS:'.symbol_select',
    STATE_SELECT_CLASS:'.state_select',
    STATE_REMOVE_CLASS:'.remove_state',
    STATE_ADD_ID:'#btn_add_state',
    STATE_MODIFYING_CLASS:"state_modifying",
    // Run control buttons
    STEP_ID:'#btn_step',
    START_ID:'#btn_start',
    RESET_ID:'#btn_reset',
    HELP_ID:'#btn_help',
    // dialogs
    HELP_DIALOG:'#help_dialog',
    // scrollers
    CURRENT_STATE_SCROLLER: '#current_state_scroller'
};

Renderer.prototype = {
    enableControl:function (id, enable) {
        control = $(id);
        if (enable) {
            control.removeAttr('disabled');
        } else {
            control.attr('disabled', 'disabled');
        }
    },

    enableRulesTableControls:function (enable) {
        this.enableControl(Renderer.ids.MOVE_SELECT_CLASS, enable);
        this.enableControl(Renderer.ids.SYMBOL_SELECT_CLASS, enable);
        this.enableControl(Renderer.ids.STATE_SELECT_CLASS, enable);
        this.enableControl(Renderer.ids.STATE_REMOVE_CLASS, enable);
        this.enableControl(Renderer.ids.STATE_ADD_ID, enable);
    },

    loadTaskNames:function () {
        var tasksCmb = $(Renderer.ids.TASK_SELECT_ID);
        var tasks = getAllTasks();

        for (var i = 0; i < tasks.length; ++i) {
            var taskName = tasks[i].getName();
            var option = $('<option></option>');
            option.attr('value', i);
            option.html(taskName);
            option.appendTo(tasksCmb);
        }
    },

    printTaskName:function (task) {
        $(Renderer.ids.TASK_NAME_ID).html(task.getName());
        $(Renderer.ids.TASK_DESCR_ID).html(task.getDescription());
    },

    updateMachineTapeScroll:function(cursor, leftBound, rightBound) {
        var shift = Math.abs(leftBound);
        cursor += shift;
        leftBound += shift;
        rightBound += shift;
        var scrollPercentage = (cursor <= 5 ? 0 : cursor) / (rightBound - leftBound);
        var scroll = $(Renderer.ids.CURRENT_STATE_SCROLLER);
        scroll.scrollLeft(scroll[0].scrollWidth * scrollPercentage);
    },



    calcLeftTapeBound:function(tape, cursor) {
        var leftBound = tape.isClean() ? -9 : tape.getFirstNotDefaultCharIndex() - 3;
        if (typeof(cursor) !== 'undefined') {
            leftBound = leftBound > cursor - 3 ? cursor - 3 : leftBound;
        }
        return leftBound > -9 ? -9 : leftBound;
    },

    calcRightTapeBound:function(tape, cursor) {
        var rightBound = tape.isClean() ? 9 : tape.getLastNotDefaultCharIndex() + 3;
        if (typeof(cursor) !== 'undefined') {
            rightBound = rightBound < cursor + 3 ? cursor + 3 : rightBound;
        }
        return rightBound < 9 ? 9 : rightBound;
    },

    renderMachineTape:function(tape, cursor) {
        var leftBound = this.calcLeftTapeBound(tape, cursor);
        var rightBound = this.calcRightTapeBound(tape, cursor);
        this.renderTape(Renderer.ids.TAPE_TBL_ID, tape, leftBound, rightBound, cursor);
        this.updateMachineTapeScroll(cursor, leftBound, rightBound);
    },

    renderFinalStateTable:function(tape) {
        var leftBound = this.calcLeftTapeBound(tape);
        var rightBound = this.calcRightTapeBound(tape);
        this.renderTape(Renderer.ids.FINAL_STATE_ID, tape, leftBound, rightBound);
    },

    renderTapeWithHead:function(tableid, tape, leftBound, rightBound, cursor) {
        var table = $(tableid);
        table.html('');
        var tapeRow = $('<tr></tr>').appendTo(table);

        for (var i = leftBound; i <= rightBound; ++i) {
            var td = $('<td></td>');
            td.html(tape.getSymbol(i));
            if (i === cursor) {
                td.append($('<div></div>').addClass(Renderer.ids.HEAD_INTO_CLASS));
            }
            tapeRow.append(td);
        }
    },

    renderSimpleTape:function (tableid, tape, leftBound, rightBound) {
        var finalStateTable = $(tableid);
        finalStateTable.html('');

        var tapeRow = $('<tr></tr>').appendTo(finalStateTable);

        for (var i = leftBound; i <= rightBound; ++i) {
            var td = $('<td></td>');
            td.html(tape.getSymbol(i));
            tapeRow.append(td);
        }
    },

    renderTape:function (tableid, tape, leftBound, rightBound, cursor) {
        if (typeof(cursor) !== 'undefined') {
            this.renderTapeWithHead(tableid, tape, leftBound, rightBound, cursor);
        } else {
            this.renderSimpleTape(tableid, tape, leftBound, rightBound);
        }
    },

    renderRulesTableHeader:function (machine) {
        var thead = $(Renderer.ids.RULES_TBL_THEAD_ID);
        thead.html("");

        var states = machine.getStatesInOrder();
        $("<td></td>").appendTo(thead);

        for (var i in states) {
            var state = states[i];
            if (state.isHaltState()) {
                continue;
            }

            var removeBtn = $("<button>x</button>");
            removeBtn.css("float", "right");
            removeBtn.attr("id", Renderer.ids.REMOVE_STATE_PREFIX + state.getName());
            removeBtn.attr("class", Renderer.ids.REMOVE_STATE_CLASS);
            removeBtn.click(function() {
                getMachineStatesFromDOM();
                machine.removeState($(this).attr("id").substr(Renderer.ids.REMOVE_STATE_PREFIX.length));
                renderer.renderRulesTable(machine);
            });

            var td = $("<td></td>");
            td.html(state.getName());
            td.addClass(Renderer.ids.STATE_NAME_CLASS);
            td.attr("name", state.getName());
            td.append(removeBtn);
            td.appendTo(thead);
        }
    },

    prepareMoveCmb:function () {
        var select_move = $("<select></select>");
        select_move.addClass(Renderer.ids.MOVE_CMB_CLASS);
        select_move.addClass(Renderer.ids.STATE_MODIFYING_CLASS);
        $("<option></option>").attr("value", "left").html("Left").appendTo(select_move);
        $("<option></option>").attr("value", "right").html("Right").appendTo(select_move);

        return select_move;
    },

    prepareSymbolCmd:function (machine) {
        var symbols = machine.getAlphabet().getSymbols();

        var select_symbol = $("<select></select>");
        select_symbol.addClass(Renderer.ids.SYMBOL_CMB_CLASS);
        select_symbol.addClass(Renderer.ids.STATE_MODIFYING_CLASS);
        for (var i in symbols) {
            var symbol = symbols[i];
            var option = $("<option></option>");
            option.attr("value", symbol);
            option.html(symbol);
            option.appendTo(select_symbol);
        }

        return select_symbol;
    },

    prepareStateCmb:function (machine) {
        var states = machine.getStates();

        var select_state = $("<select></select>");
        select_state.addClass(Renderer.ids.STATE_CMB_CLASS);
        select_state.addClass(Renderer.ids.STATE_MODIFYING_CLASS);
        for (var i in states) {
            if (states[i].isHaltState()) {
                continue;
            }
            var stateName = states[i].getName();
            var option = $('<option></option>');
            option.attr('value', stateName);
            option.html(stateName);
            option.appendTo(select_state);
        }
        $("<option></option>").attr("value", "HALT").html("HALT").appendTo(select_state);

        return select_state;
    },

    renderRulesTableBody:function (machine) {
        var tbody = $(Renderer.ids.RULES_TBL_TBODY_ID);
        tbody.html("");
        var select_move = this.prepareMoveCmb();
        var select_symbol = this.prepareSymbolCmd(machine);
        var select_state = this.prepareStateCmb(machine);

        var states = machine.getStatesInOrder();
        var symbols = machine.getAlphabet().getSymbols();
        var scnt = 0;
        for(var i in states)
            ++scnt;
        if (scnt==1)
            return;
        for (var i in symbols) {
            var symbol = symbols[i];
            var tr = $("<tr></tr>");
            tr.appendTo(tbody);
            var td = $("<td></td>").html(symbol);
            tr.append(td);
            for (var j in states) {
                if (states[j].isHaltState()) {
                    continue;
                }
                var td = $("<td></td>");
                td.attr("id", Renderer.ids.RULES_TBL_TD_STATE + states[j].getName() + Renderer.ids.RULES_TBL_TD_SYMBOL + symbol);
                td.appendTo(tr);

                var rule = states[j].getRule(symbol);
                var select_move_clone = select_move.clone();
                select_move_clone.val(rule.getMoveType() === Rule.MoveType.LEFT ? "left" : "right");
                var select_symbol_clone = select_symbol.clone();
                select_symbol_clone.val(rule.getSymbolToWrite());
                var select_state_clone = select_state.clone();
                select_state_clone.val(rule.getTargetStateName());
                td.append(select_move_clone);
                td.append(select_symbol_clone);
                td.append(select_state_clone);
            }
        }
    },

    renderRulesTable:function (machine) {
        this.renderRulesTableHeader(machine);
        this.renderRulesTableBody(machine);
    }
};
